/*
 * Copyright 2025 the original author or authors.
 * <p>
 * Licensed under the Moderne Source Available License (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * https://docs.moderne.io/licensing/moderne-source-available-license
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import {withDir} from "tmp-promise";
import * as fsp from "fs/promises";
import * as path from "path";
import {
    detectPackageManager,
    getAllLockFileNames,
    getLockFileName,
    getLockFileDetectionConfig
} from "../../src/javascript/package-manager";
import {PackageManager} from "../../src/javascript/node-resolution-result";

describe("detectPackageManager", () => {

    test("detects npm from package-lock.json", async () => {
        await withDir(async (dir) => {
            await fsp.writeFile(path.join(dir.path, "package-lock.json"), "{}");

            const result = detectPackageManager(dir.path);

            expect(result).toBe(PackageManager.Npm);
        }, {unsafeCleanup: true});
    });

    test("detects pnpm from pnpm-lock.yaml", async () => {
        await withDir(async (dir) => {
            await fsp.writeFile(path.join(dir.path, "pnpm-lock.yaml"), "lockfileVersion: 6.0");

            const result = detectPackageManager(dir.path);

            expect(result).toBe(PackageManager.Pnpm);
        }, {unsafeCleanup: true});
    });

    test("detects bun from bun.lock", async () => {
        await withDir(async (dir) => {
            await fsp.writeFile(path.join(dir.path, "bun.lock"), "{}");

            const result = detectPackageManager(dir.path);

            expect(result).toBe(PackageManager.Bun);
        }, {unsafeCleanup: true});
    });

    test("detects Yarn Classic from yarn.lock without __metadata:", async () => {
        await withDir(async (dir) => {
            const yarnClassicLock = `
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1

lodash@^4.17.21:
  version "4.17.21"
  resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz"
  integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
`;
            await fsp.writeFile(path.join(dir.path, "yarn.lock"), yarnClassicLock);

            const result = detectPackageManager(dir.path);

            expect(result).toBe(PackageManager.YarnClassic);
        }, {unsafeCleanup: true});
    });

    test("detects Yarn Berry from yarn.lock with __metadata:", async () => {
        await withDir(async (dir) => {
            const yarnBerryLock = `
# This file is generated by running "yarn install" inside your project.
# Manual changes might be lost - proceed with caution!

__metadata:
  version: 6
  cacheKey: 8

"lodash@npm:^4.17.21":
  version: 4.17.21
  resolution: "lodash@npm:4.17.21"
  checksum: eb835a2e51
`;
            await fsp.writeFile(path.join(dir.path, "yarn.lock"), yarnBerryLock);

            const result = detectPackageManager(dir.path);

            expect(result).toBe(PackageManager.YarnBerry);
        }, {unsafeCleanup: true});
    });

    test("returns undefined when no lock file exists", async () => {
        await withDir(async (dir) => {
            // Empty directory, no lock file

            const result = detectPackageManager(dir.path);

            expect(result).toBeUndefined();
        }, {unsafeCleanup: true});
    });

    test("respects priority order when multiple lock files exist", async () => {
        await withDir(async (dir) => {
            // Create both npm and yarn lock files
            await fsp.writeFile(path.join(dir.path, "package-lock.json"), "{}");
            await fsp.writeFile(path.join(dir.path, "yarn.lock"), "# yarn lockfile v1");

            const result = detectPackageManager(dir.path);

            // npm should win because package-lock.json has higher priority
            expect(result).toBe(PackageManager.Npm);
        }, {unsafeCleanup: true});
    });
});

describe("getLockFileName", () => {

    test("returns correct lock file for each package manager", () => {
        expect(getLockFileName(PackageManager.Npm)).toBe("package-lock.json");
        expect(getLockFileName(PackageManager.Pnpm)).toBe("pnpm-lock.yaml");
        expect(getLockFileName(PackageManager.YarnClassic)).toBe("yarn.lock");
        expect(getLockFileName(PackageManager.YarnBerry)).toBe("yarn.lock");
        expect(getLockFileName(PackageManager.Bun)).toBe("bun.lock");
    });
});

describe("getAllLockFileNames", () => {

    test("returns all supported lock file names", () => {
        const lockFiles = getAllLockFileNames();

        expect(lockFiles).toContain("package-lock.json");
        expect(lockFiles).toContain("yarn.lock");
        expect(lockFiles).toContain("pnpm-lock.yaml");
        expect(lockFiles).toContain("bun.lock");
    });
});

describe("getLockFileDetectionConfig", () => {

    test("returns config with preferNodeModules for yarn and pnpm", () => {
        const config = getLockFileDetectionConfig();

        const yarnConfig = config.find(c => c.filename === "yarn.lock");
        const pnpmConfig = config.find(c => c.filename === "pnpm-lock.yaml");
        const npmConfig = config.find(c => c.filename === "package-lock.json");

        expect(yarnConfig?.preferNodeModules).toBe(true);
        expect(pnpmConfig?.preferNodeModules).toBe(true);
        expect(npmConfig?.preferNodeModules).toBeUndefined();
    });
});
